package com.niit.onlinemusicserver.algorithm;

import com.niit.onlinemusicserver.model.domain.Collect;
import com.niit.onlinemusicserver.model.domain.DownloadRecord;
import com.niit.onlinemusicserver.model.domain.PayItem;
import com.niit.onlinemusicserver.model.domain.PlayRecord;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;

/**
 * 数据预处理
 */
public class DataTranslate {
    private final static float PLAY_SCORE = 1f;
    private final static float DOWNLOAD_SCORE = 2f;
    private final static float COLLECTION_SCORE = 5f;
    private final static float MAX_SCORE = 10f;
    private final static int SONG_ID_SET_KEY = 0;
    private final static float PAY_SCORE = 7f;
    /**
     * 构建用户频率矩阵来近似用户评分，对于某些系统而言，我们是不可能获取到用户对某些项目的评分的，但是我们可以利用用户的
     * 行为习惯来反映用户的“评分”，比如一个用户常常收听某一首歌，那么我们可以推断该用户喜欢该歌曲的可能性很大.
     * 总分10分，主动播放一次1分，下载2分，收藏5分，如果超过10分，按10分计算.
     *
     * @param userIdList     用户Id列表
     * @param songIdList     歌曲Id列表
     * @param downloadList   用户的下载记录列表
     * @param playList       用户的播放记录列表
     * @param collectionList 用户的收藏记录列表
     * @return 用户Id-歌曲Id 频率矩阵
     */
    public static Map<Integer, float[]> getFrequencyMatrix(List<Integer> userIdList, final List<Integer> songIdList,
                                                           List<DownloadRecord> downloadList, List<PlayRecord> playList, List<Collect> collectionList,List<PayItem> payItemList) {
        final Map<Integer, float[]> user2songRatingMatrix = new HashMap<Integer, float[]>();
        final int songLen = songIdList.size();
        //获取用户-歌曲 下载映射
        final Map<Integer, Map<Integer, Set<Integer>>> userId2songIdDownloadMap = getUserId2songIdRecordMap(downloadList, false);
        //获取用户-歌曲 收藏映射
        final Map<Integer, Map<Integer, Set<Integer>>> userId2songIdCollectionMap = getUserId2songIdRecordMap(collectionList, false);
        //获取用户-歌曲-次数 播放映射
        final Map<Integer, Map<Integer, Set<Integer>>> userId2songIdPlayMap = getUserId2songIdRecordMap(playList, true);
        //新增：获取用户-歌曲-次数 购买映射
        final Map<Integer, Map<Integer, Set<Integer>>> userId2songIdPayMap = getUserId2songIdRecordMap(payItemList, true);
        userIdList.forEach(new Consumer<Integer>() {

            public void accept(Integer userId) {
                float[] curUserRatingArray = new float[songLen];
                int songIndex = 0;
                //处理每一首歌曲
                for (Integer songId : songIdList) {
                    /**
                     * 处理下载，这里不考虑下载次数
                     */
                    if (userId2songIdDownloadMap.get(userId) != null && userId2songIdDownloadMap.get(userId).get(SONG_ID_SET_KEY).contains(songId)) {
                        //当前用户下载过的歌曲
                        curUserRatingArray[songIndex] += DOWNLOAD_SCORE;
                    }

                    /**
                     * 处理收藏，这里没有次数
                     */
                    if (userId2songIdCollectionMap.get(userId) != null && userId2songIdCollectionMap.get(userId).get(SONG_ID_SET_KEY).contains(songId)) {
                        //当前用户收藏的歌曲
                        curUserRatingArray[songIndex] += COLLECTION_SCORE;
                    }

                    /**
                     * 处理播放，考虑播放次数
                     */
                    if (userId2songIdPlayMap.get(userId) != null && userId2songIdPlayMap.get(userId).get(SONG_ID_SET_KEY).contains(songId)) {
                        //当前用户播放过的歌曲
                        int count = userId2songIdPlayMap.get(userId).get(songId).iterator().next();
                        curUserRatingArray[songIndex] += PLAY_SCORE * count;
                    }

                    /**
                     * 处理最大得分，超过最大得分，记为最大得分
                     */
                    if (curUserRatingArray[songIndex] > MAX_SCORE) {
                        curUserRatingArray[songIndex] = MAX_SCORE;
                    }

                    /**
                     * 新增：处理购买，不考虑次数
                     */
                    if (userId2songIdPayMap.get(userId) != null && userId2songIdPayMap.get(userId).get(SONG_ID_SET_KEY).contains(songId)) {
                        //当前用户收藏的歌曲
                        curUserRatingArray[songIndex] += PAY_SCORE;
                    }
                    //处理下一首歌
                    songIndex++;
                }
                //处理完一个用户
                user2songRatingMatrix.put(userId, curUserRatingArray);
            }

        });
        return user2songRatingMatrix;
    }

    /**
     * 获取用户Id - 歌曲Id 的映射Map
     * @param recordList 包含userId，songId的记录列表
     * @param isCount    是否需要计数。如果true，则Integer[1]存放计数。
     * @return 两层Map
     * 第一层Map<Integer,Map> 每个userId拥有一个自己的Map：
     *  userId,userSetMap
     * 第二层Map<Integer,Set> 用户自己的Map里面存放两个东西：
     * （1）为每首歌曲计数songId,CountSet；
     * （2）存放出现过的歌曲songSetFlag,SongIdSet：
     */
    private static <T> Map<Integer, Map<Integer, Set<Integer>>> getUserId2songIdRecordMap(final List<T> recordList, final boolean isCount) {
        final Map<Integer, Map<Integer, Set<Integer>>> userId2songIdRecordMap = new HashMap<Integer, Map<Integer, Set<Integer>>>();

        recordList.forEach(new Consumer<T>() {

            /**
             * forEach（）方法是Iterable<T>接口中的一个方法。
             * Java容器中，所有的Collection子类（List、Set）会实现Iteratable接口以实现foreach功能。
             * forEach（）方法里面有个Consumer类型，它是Java8新增的一个消费型函数式接口，
             * 其中的accept(T t)方法代表了接受一个输入参数并且无返回的操作。
             * @param t
             */
            public void accept(T t) {
                try {
                    //利用反射获和泛型获取不同类型表的相同属性
                    //经常需要对特定对象转换成我想要的JSON对象，为了实现通用性想到用反射去实现这个过程。
                    //获取当前对象的成员变量的类型
                    //对成员变量重新设值
                    Field userIdField = t.getClass().getDeclaredField("userId");
                    Field songIdField = t.getClass().getDeclaredField("songId");
                    userIdField.setAccessible(true);
                    songIdField.setAccessible(true);
                    int userId = (Integer)userIdField.get(t);
                    int songId = (Integer)songIdField.get(t);
                    //不需要计数
                    if (!isCount) {
                        //map外层的userId已经存在
                        if (userId2songIdRecordMap.containsKey(userId)) {
                            //获取当前用户的记录集合Map
                            Map<Integer, Set<Integer>> curRecordSetMap = userId2songIdRecordMap.get(userId);
                            //将当前歌曲添加到当前用户的记录集合中
                            curRecordSetMap.get(SONG_ID_SET_KEY).add(songId);
                        } else {
                            Map<Integer, Set<Integer>> curRecordSetMap = new HashMap<Integer, Set<Integer>>();
                            //创建记录歌曲Id的集合
                            Set<Integer> curSongIdSet = new HashSet<Integer>();
                            curSongIdSet.add(songId);
                            curRecordSetMap.put(SONG_ID_SET_KEY, curSongIdSet);
                            userId2songIdRecordMap.put(userId, curRecordSetMap);
                        }
                    } else {  // 需要计数
                        //map外层的userId已经存在
                        if (userId2songIdRecordMap.containsKey(userId)) {
                            //获取当前用户的记录集合Map
                            Map<Integer, Set<Integer>> curRecordSetMap = userId2songIdRecordMap.get(userId);
                            //将当前歌曲添加到当前用户的记录集合中
                            curRecordSetMap.get(SONG_ID_SET_KEY).add(songId);
                            //计数
                            count(songId, curRecordSetMap);
                        } else {
                            Map<Integer, Set<Integer>> curRecordSetMap = new HashMap<Integer, Set<Integer>>();
                            //创建记录歌曲Id的集合
                            Set<Integer> curSongIdSet = new HashSet<Integer>();
                            curSongIdSet.add(songId);
                            curRecordSetMap.put(SONG_ID_SET_KEY, curSongIdSet);
                            userId2songIdRecordMap.put(userId, curRecordSetMap);
                            //计数
                            count(songId, curRecordSetMap);
                        }
                    }

                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            private void count(int songId, Map<Integer, Set<Integer>> curRecordSetMap) {
                /**
                 * 计数,如果Map<songId,count>已经存在，则直接计数+1
                 */
                if (curRecordSetMap.containsKey(songId)) {
                    //获取当前用户歌曲的计数集合(只有一个元素)
                    Set<Integer> curCountSet = curRecordSetMap.get(songId);
                    int cnt = curCountSet.iterator().next() + 1;
                    curCountSet.clear();
                    curCountSet.add(cnt);
                } else {
                    Set<Integer> curCountSet = new HashSet<Integer>();
                    curCountSet.add(1);
                    curRecordSetMap.put(songId, curCountSet);
                }
            }
        });
        return userId2songIdRecordMap;
    }
}
